package top.healthylife.gateway.platform.anxinsign.api.util;

import cpcn.dsp.institution.api.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * @Author: GuoZi
 * @Date: 2021/8/3 20:11
 */
public class ExtEncryptAndDecrypt {

    private PrivateKey privateKey;
    private X509Certificate x509Certificate;
    public String requestSignSN;
    private PublicKey publicKey;
    private X509Certificate x509Certificate2;
    public String requestEncryptSN;

    private ExtEncryptAndDecrypt(InputStream myKeystoreStream, String myKeystorePassword, InputStream certificateStream) throws Exception {
        if (myKeystoreStream != null && certificateStream != null) {
            this.initPfxSingner(myKeystoreStream, myKeystorePassword);
            this.initPublicKey(certificateStream);
            Security.addProvider(new BouncyCastleProvider());
        } else {
            throw new RuntimeException("加载失败：stream is null ");
        }
    }

    public static ExtEncryptAndDecrypt initByStream(InputStream myKeystoreStream, String myKeystorePassword, InputStream certificateStream) throws Exception {
        return new ExtEncryptAndDecrypt(myKeystoreStream, myKeystorePassword, certificateStream);
    }

    public static void check(InputStream pfxStream, String keystore, InputStream cerStream) throws Exception {
        new ExtEncryptAndDecrypt(pfxStream, keystore, cerStream);
    }

    private void initPfxSingner(InputStream myKeystoreStream, String password) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(myKeystoreStream, password.toCharArray());
        Enumeration<String> aliases = keyStore.aliases();
        String alias = (String) aliases.nextElement();
        this.privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
        this.x509Certificate = (X509Certificate) keyStore.getCertificate(alias);
        this.requestSignSN = String.valueOf(this.x509Certificate.getSerialNumber());
    }

    private void initPublicKey(InputStream certificateStream) throws Exception {
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        this.x509Certificate2 = (X509Certificate) cf.generateCertificate(certificateStream);
        this.publicKey = this.x509Certificate2.getPublicKey();
        this.requestEncryptSN = String.valueOf(this.x509Certificate2.getSerialNumber());
    }

    public static String randomHexString(int len) {
        StringBuffer result = new StringBuffer();
        Random random = new Random();

        for (int i = 0; i < len; ++i) {
            result.append(Integer.toHexString(random.nextInt(16)));
        }

        return result.toString().toUpperCase();
    }

    public String encryptByRSA(byte[] plainData) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(1, this.publicKey);
        byte[] output = cipher.doFinal(plainData);
        return new String(Base64.encode(output));
    }

    public static String encode(String str, String key) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        SecretKeySpec keySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
        cipher.init(1, keySpec);
        byte[] result = cipher.doFinal(str.getBytes("UTF-8"));
        return new String(Base64.encode(result));
    }

    public byte[] sign(byte[] bytes) throws Exception {
        Signature signature = Signature.getInstance("SHA1withRSA");
        signature.initSign(this.privateKey);
        signature.update(bytes);
        return signature.sign();
    }

    public String getDecryptKeyByteByRSA(String respDgtlEnvlp) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(2, this.privateKey);
        byte[] output = cipher.doFinal(Base64.decode(respDgtlEnvlp));
        return bytes2hex(output);
    }

    public static String decodeByAES(String key, String cipherText) throws Exception {
        byte[] resByte = Base64.decode(cipherText);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
        SecretKeySpec keySpec = new SecretKeySpec(hex2bytes(key), "AES");
        cipher.init(2, keySpec);
        byte[] decoded = cipher.doFinal(resByte);
        return new String(decoded, "UTF-8");
    }

    public Boolean verify(byte[] msg, String sign) throws Exception {
        byte[] signature = hex2bytes(sign);
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initVerify(this.publicKey);
        sig.update(msg);
        return sig.verify(signature);
    }

    public static String bytes2hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        String b = "";
        if (null == bytes) {
            return null;
        } else {
            for (int i = 0; i < bytes.length; ++i) {
                b = Integer.toHexString(bytes[i] & 255);
                if (b.length() == 1) {
                    result.append("0");
                }

                result.append(b);
            }

            return result.toString().toUpperCase();
        }
    }

    public static byte[] hex2bytes(String hexStringParam) {
        String hexString = hexStringParam.toUpperCase();
        char[] chars = hexString.toCharArray();
        byte[] bytes = new byte[chars.length / 2];
        int index = 0;

        for (int i = 0; i < chars.length; i += 2) {
            byte newByte = 0;
            newByte = (byte) (newByte | char2byte(chars[i]));
            newByte = (byte) (newByte << 4);
            newByte |= char2byte(chars[i + 1]);
            bytes[index] = newByte;
            ++index;
        }

        return bytes;
    }

    private static byte char2byte(char ch) {
        switch (ch) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                return Byte.parseByte(String.valueOf(ch), 16);
            case ':':
            case ';':
            case '<':
            case '=':
            case '>':
            case '?':
            case '@':
            default:
                return 0;
        }
    }

    public static String createLinkStringByGet(Map<String, String> params) throws UnsupportedEncodingException {
        List<String> keys = new ArrayList(params.keySet());
        Collections.sort(keys);
        String prestr = "";

        for (int i = 0; i < keys.size(); ++i) {
            String key = (String) keys.get(i);
            String value = (String) params.get(key);
            value = URLEncoder.encode(value, "UTF-8");
            if (i == keys.size() - 1) {
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }

        return prestr;
    }

}
